The parent class Card is an abstract class and
therefore cannot be instantiated.
You can't do the following:
   . . . .
  public static void main ( String[] args ) throws IOException
  {
     . . . . 
    Card card = new Card() ;        // can't instantiate abstract class
    card.greeting() ;
     . . . .
  }
Because Card is an abstract class,
the compiler flags this as a syntax error.
Card does have a constructor that
is (implicitly) invoked by its children, but
it cannot be invoked directly.
However,
the following is OK:
   . . . .
  public static void main ( String[] args ) throws IOException
  {
     . . . . 
    Card card = new Valentine( "Joe", 14 ) ;      // a Valentine is-a Card
    card.greeting() ;
     . . . .
  }
It is OK to save a reference to a Valentine object in a
reference variable of type Card
because Valentine is-a Card.
You can think of the reference variable Card as being
a card rack designed to hold any type of a Card.